Date

2022-02-022

Rough workflow

  1. Re-quantify with kallisto (as I need the unspliced (intronic) quant)
  2. Take the h5ad objects and re-create with your UMAP view
  3. Run scVelo velocity analysis
  4. Make plots and briefly discuss results

R load

suppressPackageStartupMessages({
    library("tidyverse")
    library("reticulate")
    library("ggplot2")
    library("SingleCellExperiment")
    library("scater")
    library("Seurat")
    library("SeuratDisk")
    library("zellkonverter")
})
reticulate::repl_python()
Python 3.9.4 (/Users/mcgaugheyd/git/chick_analysis_collab/renv/python/virtualenvs/renv-python-3.9/bin/python)
Reticulate 1.24 REPL -- A Python interpreter in R.
Enter 'exit' or 'quit' to exit the REPL and return to R.
quit
# load('~/data/chick_miruna/08052021.RData')
# save just combined to reduce load time from Miruna's entire environment
# save(combined, file = '~/data/chick_miruna/combined_mgg_08052021.Rdata' )
load('~/data/chick_miruna/combined_mgg_08052021.Rdata')
combined
An object of class Seurat 
20555 features across 17295 samples within 2 assays 
Active assay: RNA (18555 features, 0 variable features)
 1 other assay present: integrated
 2 dimensional reductions calculated: pca, umap
color_palette <- pals::glasbey()[1:14]
names(color_palette) <- seq(0,13, 1)

Python load

reticulate::repl_python()
import scvelo as scv
import scanpy as sc
import cellrank as cr

Convert kallisto h5ad to h5Seurat

Convert('~/data/chick_miruna/M006_adata.h5ad', dest = "h5seurat", overwrite = TRUE)
Convert('~/data/chick_miruna/M007_adata.h5ad', dest = "h5seurat", overwrite = TRUE)
Convert('~/data/chick_miruna/M008_adata.h5ad', dest = "h5seurat", overwrite = TRUE)

Import in h5Seurat and bring over MGT info

m006 <- LoadH5Seurat('~/data/chick_miruna/M006_adata.h5seurat')
m007 <- LoadH5Seurat('~/data/chick_miruna/M007_adata.h5seurat')
m008 <- LoadH5Seurat('~/data/chick_miruna/M008_adata.h5seurat')

m006 <- RenameCells(m006, new.names = paste0(colnames(m006), '-1_1'))
m007 <- RenameCells(m007, new.names = paste0(colnames(m007), '-1_2'))
m008 <- RenameCells(m008, new.names = paste0(colnames(m008), '-1_3'))

mAll <- merge(m006, y = c(m007, m008))
# remove cells in combined but not in mAll to facilate pca/umap/HVG transfer
cells_between <- colnames(combined)[colnames(combined) %in% colnames(mAll)]

combined_sub <- subset(combined, cells = cells_between)
mAll <- subset(mAll, cells = cells_between)

mAll@meta.data <- combined_sub@meta.data

# copy over PCA and UMAP
mAll[['integrated']] <- combined_sub[['integrated']]
mAll[['umap']] <- combined_sub[['umap']]
pca <- combined_sub@reductions$pca@cell.embeddings
mAll[['pca']] <- CreateDimReducObject(
    embeddings = pca,
    key = 'PC_',
    assay = 'integrated',
    global = TRUE
  )




SaveH5Seurat(mAll, filename = "~/data/chick_miruna/mAll.h5seurat", overwrite = TRUE)
Convert("~/data/chick_miruna/mAll.h5seurat", dest = "h5ad", overwrite = TRUE)

# make sample specific adata
m006 <- subset(mAll, cluster.condition == 'Sall1 CTRL_Sall1 CTRL')
m007 <- subset(mAll, cluster.condition == 'Sall1 KO15_Sall1 KO15')
m008 <- subset(mAll, cluster.condition == 'Sall1 KO16_Sall1 KO16')

SaveH5Seurat(m006, filename = "~/data/chick_miruna/m006.h5seurat", overwrite = TRUE)
SaveH5Seurat(m007, filename = "~/data/chick_miruna/m007.h5seurat", overwrite = TRUE)
SaveH5Seurat(m008, filename = "~/data/chick_miruna/m008.h5seurat", overwrite = TRUE)

Convert("~/data/chick_miruna/m006.h5seurat", dest = "h5ad", overwrite = TRUE)
Convert("~/data/chick_miruna/m007.h5seurat", dest = "h5ad", overwrite = TRUE)
Convert("~/data/chick_miruna/m008.h5seurat", dest = "h5ad", overwrite = TRUE)

Run scVelo

def run_scVelo(adata, adata_out, redo_pca):
    adata = sc.read_h5ad(adata)
    print(adata)
    if redo_pca:
      print("\nRunning HVG and PCA\n")
      scv.pp.filter_and_normalize(adata, min_shared_counts=20, n_top_genes=10000)
      sc.tl.pca(adata)
    sc.pp.neighbors(adata, n_pcs=30, n_neighbors=30)
    # sc.tl.umap(adata)
    scv.pp.moments(adata, n_pcs=None, n_neighbors=None)
    print("Recover dynamics")
    scv.tl.recover_dynamics(adata, n_jobs=12)
    scv.tl.velocity(adata, mode="dynamical")
    scv.tl.velocity_graph(adata, n_jobs=4)
    # https://github.com/theislab/scvelo/issues/255#issuecomment-739995301
    adata.__dict__['_raw'].__dict__['_var'] = adata.__dict__['_raw'].__dict__['_var'].rename(columns={'_index': 'features'})
    adata.write_h5ad(adata_out)
    return('Done')
  
  
    
run_scVelo('/Users/mcgaugheyd/data/chick_miruna/m006.h5ad', '/Users/mcgaugheyd/data/chick_miruna/m006_scVelo.h5ad', True)
run_scVelo('/Users/mcgaugheyd/data/chick_miruna/m007.h5ad', '/Users/mcgaugheyd/data/chick_miruna/m007_scVelo.h5ad', True)
run_scVelo('/Users/mcgaugheyd/data/chick_miruna/m008.h5ad', '/Users/mcgaugheyd/data/chick_miruna/m008_scVelo.h5ad', True)

Create scVelo Plots

scv.set_figure_params()
color_palette=["#0000FF",  "#FF0000", "#00FF00", "#000033", "#FF00B6", "#005300", "#FFD300", "#009FFF", "#9A4D42", "#00FFBE", "#783FC1", "#1F9698", "#FFACFD", "#B1CC71"]

def scVelo_plotting(adata_path, prefix):
  adata = sc.read_h5ad(adata_path)
  scv.tl.velocity_embedding(adata, basis="umap")
  scv.pl.velocity_embedding_stream(adata, basis="umap", legend_fontsize=12,  color='seurat_clusters', save= prefix + '.embedding_stream.png', show = False, dpi = 300, palette=color_palette)
  scv.pl.velocity(adata, ['SALL1'], show = False, save = prefix + '.SALL1.png', dpi = 300, color ='seurat_clusters',  palette=color_palette)
  scv.tl.rank_velocity_genes(adata, groupby='seurat_clusters', min_corr=.3)
  df = scv.DataFrame(adata.uns['rank_velocity_genes']['names'])
  df.to_csv(prefix + '.rank_velocity.csv')
  adata.var.to_csv(prefix + '.var.csv')
  scv.tl.paga(adata, groups='seurat_clusters')
  scv.pl.paga(adata, basis='umap', size=20, alpha=.3, color = 'seurat_clusters',
            min_edge_width=2, node_size_scale=1.5, show = False,
            dpi = 300, save = prefix + '.PAGA.png', palette=color_palette)

scVelo_plotting('/Users/mcgaugheyd/data/chick_miruna/m006_scVelo.h5ad', 'WT')

Recolored your UMAPs

DimPlot(combined, label = TRUE, label.box = TRUE, split.by = 'cluster.condition', pt.size = 1) + scale_color_manual(values = color_palette) + scale_fill_manual(values = alpha(rep('white', 14), 0.9))
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.

DimPlot(combined, split.by = 'cluster.condition',group.by = 'Phase' ) + scale_color_manual(values = pals::brewer.set2(5) %>% unname())

Overall Velocity

WT, G15, then G16

Velocity is the relationship between unspliced (the “future”) and spliced (the “present”) mRNA. If there is a higher ratio of unspliced to spliced of a transcript, then presumably in the future the cell will be splicing and using more of the that transcript. Vice versa for higher ratios of spliced / unspliced. By linking nearby cells (graph) and the velocities you can also infer direction.

knitr::include_graphics("figures/scvelo_WT.embedding_stream.png")

knitr::include_graphics("figures/scvelo_G15.embedding_stream.png")

knitr::include_graphics("figures/scvelo_G16.embedding_stream.png")

Diff Velocity

You can take each cluster and identify genes whose velocity differs between them

velo %>% mutate(Cluster = as.integer(Cluster)) %>% group_by(Cluster, Gene) %>% summarise(`Number of Samples with Diff Velocity` = n(), `Samples with Diff Velocity` = paste(Sample, collapse = ', ')) %>% DT::datatable(filter = 'top', options = list(
  pageLength = 20, autoWidth = TRUE)) 
`summarise()` has grouped output by 'Cluster'. You can override using the `.groups` argument.

Let’s look at DPYSL3, which is listed for cluster 2, G15 and G16 (and not WT)

gene = 'DPYSL3'
for prefix in ['WT', 'G15','G16']:
  print(prefix)
  if prefix == 'WT':
    file = '/Users/mcgaugheyd/data/chick_miruna/m006_scVelo.h5ad'
  if prefix == 'G15':
    file = '/Users/mcgaugheyd/data/chick_miruna/m007_scVelo.h5ad'
  if prefix == 'G16':
    file = '/Users/mcgaugheyd/data/chick_miruna/m008_scVelo.h5ad'
  adata = sc.read_h5ad(file)
  scv.pl.velocity(adata, [gene], show = False, save = prefix +  '.' + gene + '.png', dpi = 300, color ='seurat_clusters',  palette=color_palette)
WT
saving figure to file ./figures/scvelo_WT.DPYSL3.png
<AxesSubplot:title={'center':'expression'}>
G15
saving figure to file ./figures/scvelo_G15.DPYSL3.png
<AxesSubplot:title={'center':'expression'}>
G16
saving figure to file ./figures/scvelo_G16.DPYSL3.png
<AxesSubplot:title={'center':'expression'}>

Again UMAP for your reference

DimPlot(combined, label = TRUE, label.box = TRUE, split.by = 'cluster.condition', pt.size = 1) + scale_color_manual(values = color_palette) + scale_fill_manual(values = alpha(rep('white', 14), 0.9))
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.

DPLYSL3 velocity drops in cluster 2 in the WT, but is maintained in the G15/G16. We also see reduced velocity in the G15/G16 in the top right branch, while the WT maintains the velocity.

DPYSL3 has a role in migration and cell cycle

SALL1

WT, G15, then G16

No huge differences in velocity characteristics. Quick viz explainer for velocity:

We we see a negative velocity of SALL1 in cluster 7 in the WT which is not present in the mutants.

Overall SALL1 velocity (and expression) appears lower in the G15/G16 relative to the WT.

knitr::include_graphics("figures/scvelo_WT.SALL1.png")

knitr::include_graphics("figures/scvelo_G15.SALL1.png")

knitr::include_graphics("figures/scvelo_G16.SALL1.png")

WT, then G15, then G16 PAGA Cluster Trajectory Graph

Relationships between clusters (PAGA algorithm attempts to infer differentiation path)

This will be more informative when the cluster names are swapped out for stuff like “progenitors” and “cones”

This also may be total trash - I can’t remember what cluster is doing what…so perhaps these relationships make no sense?

knitr::include_graphics("figures/scvelo_WT.PAGA.png")

knitr::include_graphics("figures/scvelo_G15.PAGA.png")

knitr::include_graphics("figures/scvelo_G16.PAGA.png")

LS0tCnRpdGxlOiAiTWlydW5hIEdUIENoaWNrIFRyYWplY3RvcnkgQW5hbHlzaXMiCmF1dGhvcjogRGF2aWQgTWNHYXVnaGV5CmRhdGU6IDIwMjItMDItMDQKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdGhlbWU6IHVuaXRlZAotLS0KCiMgRGF0ZQoyMDIyLTAyLTAyMgoKIyBSb3VnaCB3b3JrZmxvdwoxLiBSZS1xdWFudGlmeSB3aXRoIGthbGxpc3RvIChhcyBJIG5lZWQgdGhlIHVuc3BsaWNlZCAoaW50cm9uaWMpIHF1YW50KQoyLiBUYWtlIHRoZSBoNWFkIG9iamVjdHMgYW5kIHJlLWNyZWF0ZSB3aXRoIHlvdXIgVU1BUCB2aWV3CjMuIFJ1biBbc2NWZWxvXShodHRwczovL3NjdmVsby5yZWFkdGhlZG9jcy5pbykgdmVsb2NpdHkgYW5hbHlzaXMKNC4gTWFrZSBwbG90cyBhbmQgYnJpZWZseSBkaXNjdXNzIHJlc3VsdHMKCiMgUiBsb2FkCmBgYHtyfQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoewogICAgbGlicmFyeSgidGlkeXZlcnNlIikKICAgIGxpYnJhcnkoInJldGljdWxhdGUiKQogICAgbGlicmFyeSgiZ2dwbG90MiIpCiAgICBsaWJyYXJ5KCJTaW5nbGVDZWxsRXhwZXJpbWVudCIpCiAgICBsaWJyYXJ5KCJzY2F0ZXIiKQogICAgbGlicmFyeSgiU2V1cmF0IikKICAgIGxpYnJhcnkoIlNldXJhdERpc2siKQogICAgbGlicmFyeSgiemVsbGtvbnZlcnRlciIpCn0pCiMgbG9hZCgnfi9kYXRhL2NoaWNrX21pcnVuYS8wODA1MjAyMS5SRGF0YScpCiMgc2F2ZSBqdXN0IGNvbWJpbmVkIHRvIHJlZHVjZSBsb2FkIHRpbWUgZnJvbSBNaXJ1bmEncyBlbnRpcmUgZW52aXJvbm1lbnQKIyBzYXZlKGNvbWJpbmVkLCBmaWxlID0gJ34vZGF0YS9jaGlja19taXJ1bmEvY29tYmluZWRfbWdnXzA4MDUyMDIxLlJkYXRhJyApCmxvYWQoJ34vZGF0YS9jaGlja19taXJ1bmEvY29tYmluZWRfbWdnXzA4MDUyMDIxLlJkYXRhJykKY29tYmluZWQKCmNvbG9yX3BhbGV0dGUgPC0gcGFsczo6Z2xhc2JleSgpWzE6MTRdCm5hbWVzKGNvbG9yX3BhbGV0dGUpIDwtIHNlcSgwLDEzLCAxKQpgYGAKCiMgUHl0aG9uIGxvYWQKYGBge3B5dGhvbn0KaW1wb3J0IHNjdmVsbyBhcyBzY3YKaW1wb3J0IHNjYW5weSBhcyBzYwppbXBvcnQgY2VsbHJhbmsgYXMgY3IKYGBgCgojIENvbnZlcnQga2FsbGlzdG8gaDVhZCB0byBoNVNldXJhdApgYGB7cn0KQ29udmVydCgnfi9kYXRhL2NoaWNrX21pcnVuYS9NMDA2X2FkYXRhLmg1YWQnLCBkZXN0ID0gImg1c2V1cmF0Iiwgb3ZlcndyaXRlID0gVFJVRSkKQ29udmVydCgnfi9kYXRhL2NoaWNrX21pcnVuYS9NMDA3X2FkYXRhLmg1YWQnLCBkZXN0ID0gImg1c2V1cmF0Iiwgb3ZlcndyaXRlID0gVFJVRSkKQ29udmVydCgnfi9kYXRhL2NoaWNrX21pcnVuYS9NMDA4X2FkYXRhLmg1YWQnLCBkZXN0ID0gImg1c2V1cmF0Iiwgb3ZlcndyaXRlID0gVFJVRSkKYGBgCgojIEltcG9ydCBpbiBoNVNldXJhdCBhbmQgYnJpbmcgb3ZlciBNR1QgaW5mbwpgYGB7cn0KbTAwNiA8LSBMb2FkSDVTZXVyYXQoJ34vZGF0YS9jaGlja19taXJ1bmEvTTAwNl9hZGF0YS5oNXNldXJhdCcpCm0wMDcgPC0gTG9hZEg1U2V1cmF0KCd+L2RhdGEvY2hpY2tfbWlydW5hL00wMDdfYWRhdGEuaDVzZXVyYXQnKQptMDA4IDwtIExvYWRINVNldXJhdCgnfi9kYXRhL2NoaWNrX21pcnVuYS9NMDA4X2FkYXRhLmg1c2V1cmF0JykKCm0wMDYgPC0gUmVuYW1lQ2VsbHMobTAwNiwgbmV3Lm5hbWVzID0gcGFzdGUwKGNvbG5hbWVzKG0wMDYpLCAnLTFfMScpKQptMDA3IDwtIFJlbmFtZUNlbGxzKG0wMDcsIG5ldy5uYW1lcyA9IHBhc3RlMChjb2xuYW1lcyhtMDA3KSwgJy0xXzInKSkKbTAwOCA8LSBSZW5hbWVDZWxscyhtMDA4LCBuZXcubmFtZXMgPSBwYXN0ZTAoY29sbmFtZXMobTAwOCksICctMV8zJykpCgptQWxsIDwtIG1lcmdlKG0wMDYsIHkgPSBjKG0wMDcsIG0wMDgpKQojIHJlbW92ZSBjZWxscyBpbiBjb21iaW5lZCBidXQgbm90IGluIG1BbGwgdG8gZmFjaWxhdGUgcGNhL3VtYXAvSFZHIHRyYW5zZmVyCmNlbGxzX2JldHdlZW4gPC0gY29sbmFtZXMoY29tYmluZWQpW2NvbG5hbWVzKGNvbWJpbmVkKSAlaW4lIGNvbG5hbWVzKG1BbGwpXQoKY29tYmluZWRfc3ViIDwtIHN1YnNldChjb21iaW5lZCwgY2VsbHMgPSBjZWxsc19iZXR3ZWVuKQptQWxsIDwtIHN1YnNldChtQWxsLCBjZWxscyA9IGNlbGxzX2JldHdlZW4pCgptQWxsQG1ldGEuZGF0YSA8LSBjb21iaW5lZF9zdWJAbWV0YS5kYXRhCgojIGNvcHkgb3ZlciBQQ0EgYW5kIFVNQVAKbUFsbFtbJ2ludGVncmF0ZWQnXV0gPC0gY29tYmluZWRfc3ViW1snaW50ZWdyYXRlZCddXQptQWxsW1sndW1hcCddXSA8LSBjb21iaW5lZF9zdWJbWyd1bWFwJ11dCnBjYSA8LSBjb21iaW5lZF9zdWJAcmVkdWN0aW9ucyRwY2FAY2VsbC5lbWJlZGRpbmdzCm1BbGxbWydwY2EnXV0gPC0gQ3JlYXRlRGltUmVkdWNPYmplY3QoCiAgICBlbWJlZGRpbmdzID0gcGNhLAogICAga2V5ID0gJ1BDXycsCiAgICBhc3NheSA9ICdpbnRlZ3JhdGVkJywKICAgIGdsb2JhbCA9IFRSVUUKICApCgoKCgpTYXZlSDVTZXVyYXQobUFsbCwgZmlsZW5hbWUgPSAifi9kYXRhL2NoaWNrX21pcnVuYS9tQWxsLmg1c2V1cmF0Iiwgb3ZlcndyaXRlID0gVFJVRSkKQ29udmVydCgifi9kYXRhL2NoaWNrX21pcnVuYS9tQWxsLmg1c2V1cmF0IiwgZGVzdCA9ICJoNWFkIiwgb3ZlcndyaXRlID0gVFJVRSkKCiMgbWFrZSBzYW1wbGUgc3BlY2lmaWMgYWRhdGEKbTAwNiA8LSBzdWJzZXQobUFsbCwgY2x1c3Rlci5jb25kaXRpb24gPT0gJ1NhbGwxIENUUkxfU2FsbDEgQ1RSTCcpCm0wMDcgPC0gc3Vic2V0KG1BbGwsIGNsdXN0ZXIuY29uZGl0aW9uID09ICdTYWxsMSBLTzE1X1NhbGwxIEtPMTUnKQptMDA4IDwtIHN1YnNldChtQWxsLCBjbHVzdGVyLmNvbmRpdGlvbiA9PSAnU2FsbDEgS08xNl9TYWxsMSBLTzE2JykKClNhdmVINVNldXJhdChtMDA2LCBmaWxlbmFtZSA9ICJ+L2RhdGEvY2hpY2tfbWlydW5hL20wMDYuaDVzZXVyYXQiLCBvdmVyd3JpdGUgPSBUUlVFKQpTYXZlSDVTZXVyYXQobTAwNywgZmlsZW5hbWUgPSAifi9kYXRhL2NoaWNrX21pcnVuYS9tMDA3Lmg1c2V1cmF0Iiwgb3ZlcndyaXRlID0gVFJVRSkKU2F2ZUg1U2V1cmF0KG0wMDgsIGZpbGVuYW1lID0gIn4vZGF0YS9jaGlja19taXJ1bmEvbTAwOC5oNXNldXJhdCIsIG92ZXJ3cml0ZSA9IFRSVUUpCgpDb252ZXJ0KCJ+L2RhdGEvY2hpY2tfbWlydW5hL20wMDYuaDVzZXVyYXQiLCBkZXN0ID0gImg1YWQiLCBvdmVyd3JpdGUgPSBUUlVFKQpDb252ZXJ0KCJ+L2RhdGEvY2hpY2tfbWlydW5hL20wMDcuaDVzZXVyYXQiLCBkZXN0ID0gImg1YWQiLCBvdmVyd3JpdGUgPSBUUlVFKQpDb252ZXJ0KCJ+L2RhdGEvY2hpY2tfbWlydW5hL20wMDguaDVzZXVyYXQiLCBkZXN0ID0gImg1YWQiLCBvdmVyd3JpdGUgPSBUUlVFKQpgYGAKCiMgUnVuIHNjVmVsbwoKCmBgYHtweXRob259CmRlZiBydW5fc2NWZWxvKGFkYXRhLCBhZGF0YV9vdXQsIHJlZG9fcGNhKToKICAgIGFkYXRhID0gc2MucmVhZF9oNWFkKGFkYXRhKQogICAgcHJpbnQoYWRhdGEpCiAgICBpZiByZWRvX3BjYToKICAgICAgcHJpbnQoIlxuUnVubmluZyBIVkcgYW5kIFBDQVxuIikKICAgICAgc2N2LnBwLmZpbHRlcl9hbmRfbm9ybWFsaXplKGFkYXRhLCBtaW5fc2hhcmVkX2NvdW50cz0yMCwgbl90b3BfZ2VuZXM9MTAwMDApCiAgICAgIHNjLnRsLnBjYShhZGF0YSkKICAgIHNjLnBwLm5laWdoYm9ycyhhZGF0YSwgbl9wY3M9MzAsIG5fbmVpZ2hib3JzPTMwKQogICAgIyBzYy50bC51bWFwKGFkYXRhKQogICAgc2N2LnBwLm1vbWVudHMoYWRhdGEsIG5fcGNzPU5vbmUsIG5fbmVpZ2hib3JzPU5vbmUpCiAgICBwcmludCgiUmVjb3ZlciBkeW5hbWljcyIpCiAgICBzY3YudGwucmVjb3Zlcl9keW5hbWljcyhhZGF0YSwgbl9qb2JzPTEyKQogICAgc2N2LnRsLnZlbG9jaXR5KGFkYXRhLCBtb2RlPSJkeW5hbWljYWwiKQogICAgc2N2LnRsLnZlbG9jaXR5X2dyYXBoKGFkYXRhLCBuX2pvYnM9NCkKICAgICMgaHR0cHM6Ly9naXRodWIuY29tL3RoZWlzbGFiL3NjdmVsby9pc3N1ZXMvMjU1I2lzc3VlY29tbWVudC03Mzk5OTUzMDEKICAgIGFkYXRhLl9fZGljdF9fWydfcmF3J10uX19kaWN0X19bJ192YXInXSA9IGFkYXRhLl9fZGljdF9fWydfcmF3J10uX19kaWN0X19bJ192YXInXS5yZW5hbWUoY29sdW1ucz17J19pbmRleCc6ICdmZWF0dXJlcyd9KQogICAgYWRhdGEud3JpdGVfaDVhZChhZGF0YV9vdXQpCiAgICByZXR1cm4oJ0RvbmUnKQogIAogIAogICAgCnJ1bl9zY1ZlbG8oJy9Vc2Vycy9tY2dhdWdoZXlkL2RhdGEvY2hpY2tfbWlydW5hL20wMDYuaDVhZCcsICcvVXNlcnMvbWNnYXVnaGV5ZC9kYXRhL2NoaWNrX21pcnVuYS9tMDA2X3NjVmVsby5oNWFkJywgVHJ1ZSkKcnVuX3NjVmVsbygnL1VzZXJzL21jZ2F1Z2hleWQvZGF0YS9jaGlja19taXJ1bmEvbTAwNy5oNWFkJywgJy9Vc2Vycy9tY2dhdWdoZXlkL2RhdGEvY2hpY2tfbWlydW5hL20wMDdfc2NWZWxvLmg1YWQnLCBUcnVlKQpydW5fc2NWZWxvKCcvVXNlcnMvbWNnYXVnaGV5ZC9kYXRhL2NoaWNrX21pcnVuYS9tMDA4Lmg1YWQnLCAnL1VzZXJzL21jZ2F1Z2hleWQvZGF0YS9jaGlja19taXJ1bmEvbTAwOF9zY1ZlbG8uaDVhZCcsIFRydWUpCgpgYGAKCiMgQ3JlYXRlIHNjVmVsbyBQbG90cwoKCmBgYHtweXRob259CnNjdi5zZXRfZmlndXJlX3BhcmFtcygpCmNvbG9yX3BhbGV0dGU9WyIjMDAwMEZGIiwgICIjRkYwMDAwIiwgIiMwMEZGMDAiLCAiIzAwMDAzMyIsICIjRkYwMEI2IiwgIiMwMDUzMDAiLCAiI0ZGRDMwMCIsICIjMDA5RkZGIiwgIiM5QTRENDIiLCAiIzAwRkZCRSIsICIjNzgzRkMxIiwgIiMxRjk2OTgiLCAiI0ZGQUNGRCIsICIjQjFDQzcxIl0KIyBjb2xvcl9wYWxldHRlID0geycwJzogIiMwMDAwRkYiLCAnMSc6ICIjRkYwMDAwIiwnMic6ICIjMDBGRjAwIiwnMyc6ICIjMDAwMDMzIiwnNCc6ICIjRkYwMEI2IiwnNScgOiAiIzAwNTMwMCIsJzYnIDogIiNGRkQzMDAiLCc3JyA6ICIjMDA5RkZGIiwnOCcgOiAiIzlBNEQ0MiIsJzknIDogIiMwMEZGQkUiLCcxMCc6ICIjNzgzRkMxIiwnMTEnOiAiIzFGOTY5OCIsJzEyJzogIiNGRkFDRkQiLCcxMycgOiAiI0IxQ0M3MSJ9CmRlZiBzY1ZlbG9fcGxvdHRpbmcoYWRhdGFfcGF0aCwgcHJlZml4KToKICBhZGF0YSA9IHNjLnJlYWRfaDVhZChhZGF0YV9wYXRoKQogIHNjdi50bC52ZWxvY2l0eV9lbWJlZGRpbmcoYWRhdGEsIGJhc2lzPSJ1bWFwIikKICBzY3YucGwudmVsb2NpdHlfZW1iZWRkaW5nX3N0cmVhbShhZGF0YSwgYmFzaXM9InVtYXAiLCBsZWdlbmRfZm9udHNpemU9MTIsICBjb2xvcj0nc2V1cmF0X2NsdXN0ZXJzJywgc2F2ZT0gcHJlZml4ICsgJy5lbWJlZGRpbmdfc3RyZWFtLnBuZycsIHNob3cgPSBGYWxzZSwgZHBpID0gMzAwLCBwYWxldHRlPWNvbG9yX3BhbGV0dGUpCiAgc2N2LnBsLnZlbG9jaXR5KGFkYXRhLCBbJ1NBTEwxJ10sIHNob3cgPSBGYWxzZSwgc2F2ZSA9IHByZWZpeCArICcuU0FMTDEucG5nJywgZHBpID0gMzAwLCBjb2xvciA9J3NldXJhdF9jbHVzdGVycycsICBwYWxldHRlPWNvbG9yX3BhbGV0dGUpCiAgc2N2LnRsLnJhbmtfdmVsb2NpdHlfZ2VuZXMoYWRhdGEsIGdyb3VwYnk9J3NldXJhdF9jbHVzdGVycycsIG1pbl9jb3JyPS4zKQogIGRmID0gc2N2LkRhdGFGcmFtZShhZGF0YS51bnNbJ3JhbmtfdmVsb2NpdHlfZ2VuZXMnXVsnbmFtZXMnXSkKICBkZi50b19jc3YocHJlZml4ICsgJy5yYW5rX3ZlbG9jaXR5LmNzdicpCiAgYWRhdGEudmFyLnRvX2NzdihwcmVmaXggKyAnLnZhci5jc3YnKQogIHNjdi50bC5wYWdhKGFkYXRhLCBncm91cHM9J3NldXJhdF9jbHVzdGVycycpCiAgc2N2LnBsLnBhZ2EoYWRhdGEsIGJhc2lzPSd1bWFwJywgc2l6ZT0yMCwgYWxwaGE9LjMsIGNvbG9yID0gJ3NldXJhdF9jbHVzdGVycycsCiAgICAgICAgICAgIG1pbl9lZGdlX3dpZHRoPTIsIG5vZGVfc2l6ZV9zY2FsZT0xLjUsIHNob3cgPSBGYWxzZSwKICAgICAgICAgICAgZHBpID0gMzAwLCBzYXZlID0gcHJlZml4ICsgJy5QQUdBLnBuZycsIHBhbGV0dGU9Y29sb3JfcGFsZXR0ZSkKCnNjVmVsb19wbG90dGluZygnL1VzZXJzL21jZ2F1Z2hleWQvZGF0YS9jaGlja19taXJ1bmEvbTAwNl9zY1ZlbG8uaDVhZCcsICdXVCcpCnNjVmVsb19wbG90dGluZygnL1VzZXJzL21jZ2F1Z2hleWQvZGF0YS9jaGlja19taXJ1bmEvbTAwN19zY1ZlbG8uaDVhZCcsICdHMTUnKQpzY1ZlbG9fcGxvdHRpbmcoJy9Vc2Vycy9tY2dhdWdoZXlkL2RhdGEvY2hpY2tfbWlydW5hL20wMDhfc2NWZWxvLmg1YWQnLCAnRzE2JykKYGBgCgoKIyBSZWNvbG9yZWQgeW91ciBVTUFQcwpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTN9CkRpbVBsb3QoY29tYmluZWQsIGxhYmVsID0gVFJVRSwgbGFiZWwuYm94ID0gVFJVRSwgc3BsaXQuYnkgPSAnY2x1c3Rlci5jb25kaXRpb24nLCBwdC5zaXplID0gMSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY29sb3JfcGFsZXR0ZSkgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBhbHBoYShyZXAoJ3doaXRlJywgMTQpLCAwLjkpKQoKRGltUGxvdChjb21iaW5lZCwgc3BsaXQuYnkgPSAnY2x1c3Rlci5jb25kaXRpb24nLGdyb3VwLmJ5ID0gJ1BoYXNlJyApICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHBhbHM6OmJyZXdlci5zZXQyKDUpICU+JSB1bm5hbWUoKSkKYGBgCgoKIyBPdmVyYWxsIFZlbG9jaXR5CldULCBHMTUsIHRoZW4gRzE2CgpWZWxvY2l0eSBpcyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdW5zcGxpY2VkICh0aGUgImZ1dHVyZSIpIGFuZCBzcGxpY2VkICh0aGUgInByZXNlbnQiKSBtUk5BLiBJZiB0aGVyZSBpcyBhIGhpZ2hlciByYXRpbyBvZiB1bnNwbGljZWQgdG8gc3BsaWNlZCBvZiBhIHRyYW5zY3JpcHQsIHRoZW4gcHJlc3VtYWJseSBpbiB0aGUgZnV0dXJlIHRoZSBjZWxsIHdpbGwgYmUgc3BsaWNpbmcgYW5kIHVzaW5nIG1vcmUgb2YgdGhlIHRoYXQgdHJhbnNjcmlwdC4gVmljZSB2ZXJzYSBmb3IgaGlnaGVyIHJhdGlvcyBvZiBzcGxpY2VkIC8gdW5zcGxpY2VkLiBCeSBsaW5raW5nIG5lYXJieSBjZWxscyAoZ3JhcGgpIGFuZCB0aGUgdmVsb2NpdGllcyB5b3UgY2FuIGFsc28gaW5mZXIgZGlyZWN0aW9uLiAKCgpgYGB7cn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImZpZ3VyZXMvc2N2ZWxvX1dULmVtYmVkZGluZ19zdHJlYW0ucG5nIikKa25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImZpZ3VyZXMvc2N2ZWxvX0cxNS5lbWJlZGRpbmdfc3RyZWFtLnBuZyIpCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJmaWd1cmVzL3NjdmVsb19HMTYuZW1iZWRkaW5nX3N0cmVhbS5wbmciKQpgYGAKCiMgRGlmZiBWZWxvY2l0eQpZb3UgY2FuIHRha2UgZWFjaCBjbHVzdGVyIGFuZCBpZGVudGlmeSBnZW5lcyB3aG9zZSB2ZWxvY2l0eSBkaWZmZXJzIGJldHdlZW4gdGhlbQoKYGBge3J9CnZlbG9fdGFibGVfV1QgPC0gcmVhZF9jc3YoIldULnJhbmtfdmVsb2NpdHkuY3N2IikgJT4lIHBpdm90X2xvbmdlcigtYC4uLjFgKSAlPiUgZHBseXI6OnJlbmFtZShDbHVzdGVyID0gbmFtZSwgR2VuZSA9IHZhbHVlKSAlPiUgc2VsZWN0KC0uLi4xKSAlPiUgbXV0YXRlKFNhbXBsZSA9ICdXVCcpCnZlbG9fdGFibGVfRzE1IDwtIHJlYWRfY3N2KCJHMTUucmFua192ZWxvY2l0eS5jc3YiKSAlPiUgcGl2b3RfbG9uZ2VyKC1gLi4uMWApICU+JSBkcGx5cjo6cmVuYW1lKENsdXN0ZXIgPSBuYW1lLCBHZW5lID0gdmFsdWUpICU+JSBzZWxlY3QoLS4uLjEpICU+JSBtdXRhdGUoU2FtcGxlID0gJ0cxNScpCnZlbG9fdGFibGVfRzE2IDwtIHJlYWRfY3N2KCJHMTYucmFua192ZWxvY2l0eS5jc3YiKSAlPiUgcGl2b3RfbG9uZ2VyKC1gLi4uMWApICU+JSBkcGx5cjo6cmVuYW1lKENsdXN0ZXIgPSBuYW1lLCBHZW5lID0gdmFsdWUpICU+JSBzZWxlY3QoLS4uLjEpICU+JSBtdXRhdGUoU2FtcGxlID0gJ0cxNicpCnZlbG8gPC0gYmluZF9yb3dzKHZlbG9fdGFibGVfV1QsIHZlbG9fdGFibGVfRzE1LCB2ZWxvX3RhYmxlX0cxNikgJT4lIHVuaXF1ZSgpCnZlbG8gJT4lIG11dGF0ZShDbHVzdGVyID0gYXMuaW50ZWdlcihDbHVzdGVyKSkgJT4lIGdyb3VwX2J5KENsdXN0ZXIsIEdlbmUpICU+JSBzdW1tYXJpc2UoYE51bWJlciBvZiBTYW1wbGVzIHdpdGggRGlmZiBWZWxvY2l0eWAgPSBuKCksIGBTYW1wbGVzIHdpdGggRGlmZiBWZWxvY2l0eWAgPSBwYXN0ZShTYW1wbGUsIGNvbGxhcHNlID0gJywgJykpICU+JSBEVDo6ZGF0YXRhYmxlKGZpbHRlciA9ICd0b3AnLCBvcHRpb25zID0gbGlzdCgKICBwYWdlTGVuZ3RoID0gMjAsIGF1dG9XaWR0aCA9IFRSVUUpKSAKYGBgCgojIyBMZXQncyBsb29rIGF0IERQWVNMMywgd2hpY2ggaXMgbGlzdGVkIGZvciBjbHVzdGVyIDIsIEcxNSBhbmQgRzE2IChhbmQgbm90IFdUKQpgYGB7cHl0aG9ufQpnZW5lID0gJ0RQWVNMMycKZm9yIHByZWZpeCBpbiBbJ1dUJywgJ0cxNScsJ0cxNiddOgogIHByaW50KHByZWZpeCkKICBpZiBwcmVmaXggPT0gJ1dUJzoKICAgIGZpbGUgPSAnL1VzZXJzL21jZ2F1Z2hleWQvZGF0YS9jaGlja19taXJ1bmEvbTAwNl9zY1ZlbG8uaDVhZCcKICBpZiBwcmVmaXggPT0gJ0cxNSc6CiAgICBmaWxlID0gJy9Vc2Vycy9tY2dhdWdoZXlkL2RhdGEvY2hpY2tfbWlydW5hL20wMDdfc2NWZWxvLmg1YWQnCiAgaWYgcHJlZml4ID09ICdHMTYnOgogICAgZmlsZSA9ICcvVXNlcnMvbWNnYXVnaGV5ZC9kYXRhL2NoaWNrX21pcnVuYS9tMDA4X3NjVmVsby5oNWFkJwogIGFkYXRhID0gc2MucmVhZF9oNWFkKGZpbGUpCiAgc2N2LnBsLnZlbG9jaXR5KGFkYXRhLCBbZ2VuZV0sIHNob3cgPSBGYWxzZSwgc2F2ZSA9IHByZWZpeCArICAnLicgKyBnZW5lICsgJy5wbmcnLCBkcGkgPSAzMDAsIGNvbG9yID0nc2V1cmF0X2NsdXN0ZXJzJywgIHBhbGV0dGU9Y29sb3JfcGFsZXR0ZSkKCmBgYAojIyMgQWdhaW4gVU1BUCBmb3IgeW91ciByZWZlcmVuY2UKYGBge3J9CkRpbVBsb3QoY29tYmluZWQsIGxhYmVsID0gVFJVRSwgbGFiZWwuYm94ID0gVFJVRSwgc3BsaXQuYnkgPSAnY2x1c3Rlci5jb25kaXRpb24nLCBwdC5zaXplID0gMSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY29sb3JfcGFsZXR0ZSkgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBhbHBoYShyZXAoJ3doaXRlJywgMTQpLCAwLjkpKQpgYGAKRFBMWVNMMyB2ZWxvY2l0eSBkcm9wcyBpbiBjbHVzdGVyIDIgaW4gdGhlIFdULCBidXQgaXMgbWFpbnRhaW5lZCBpbiB0aGUgRzE1L0cxNi4gV2UgYWxzbyBzZWUgcmVkdWNlZCB2ZWxvY2l0eSBpbiB0aGUgRzE1L0cxNiBpbiB0aGUgdG9wIHJpZ2h0IGJyYW5jaCwgd2hpbGUgdGhlIFdUIG1haW50YWlucyB0aGUgdmVsb2NpdHkuIAoKW0RQWVNMMyBoYXMgYSByb2xlIGluIG1pZ3JhdGlvbiBhbmQgY2VsbCBjeWNsZV0oaHR0cHM6Ly93d3cucG5hcy5vcmcvY29udGVudC8xMTUvNTEvRTExOTc4KQoKIVtdKGZpZ3VyZXMvc2N2ZWxvX1dULkRQWVNMMy5wbmcpCgohW10oZmlndXJlcy9zY3ZlbG9fRzE1LkRQWVNMMy5wbmcpCiFbXShmaWd1cmVzL3NjdmVsb19HMTYuRFBZU0wzLnBuZykKCgoKCgojIFNBTEwxCldULCBHMTUsIHRoZW4gRzE2CgpObyAqaHVnZSogZGlmZmVyZW5jZXMgaW4gdmVsb2NpdHkgY2hhcmFjdGVyaXN0aWNzLiBRdWljayB2aXogZXhwbGFpbmVyIGZvciB2ZWxvY2l0eTogCiFbXShodHRwczovL3VzZXItaW1hZ2VzLmdpdGh1YnVzZXJjb250ZW50LmNvbS8zMTg4MzcxOC84MDIyNzQ1Mi1lYjgyMjQ4MC04NjRkLTExZWEtOTM5OS01Njg4NmM1ZTI3ODUuZ2lmKQoKV2Ugd2Ugc2VlIGEgKm5lZ2F0aXZlKiB2ZWxvY2l0eSBvZiBTQUxMMSBpbiBjbHVzdGVyIDcgaW4gdGhlIFdUIHdoaWNoIGlzIG5vdCBwcmVzZW50IGluIHRoZSBtdXRhbnRzLgoKT3ZlcmFsbCBTQUxMMSB2ZWxvY2l0eSAoYW5kIGV4cHJlc3Npb24pIGFwcGVhcnMgbG93ZXIgaW4gdGhlIEcxNS9HMTYgcmVsYXRpdmUgdG8gdGhlIFdULgoKCmBgYHtyfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiZmlndXJlcy9zY3ZlbG9fV1QuU0FMTDEucG5nIikKa25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImZpZ3VyZXMvc2N2ZWxvX0cxNS5TQUxMMS5wbmciKQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiZmlndXJlcy9zY3ZlbG9fRzE2LlNBTEwxLnBuZyIpCmBgYAoKIyBXVCwgdGhlbiBHMTUsIHRoZW4gRzE2IFBBR0EgQ2x1c3RlciBUcmFqZWN0b3J5IEdyYXBoClJlbGF0aW9uc2hpcHMgYmV0d2VlbiBjbHVzdGVycyAoUEFHQSBhbGdvcml0aG0gYXR0ZW1wdHMgdG8gaW5mZXIgZGlmZmVyZW50aWF0aW9uIHBhdGgpCgpUaGlzIHdpbGwgYmUgbW9yZSBpbmZvcm1hdGl2ZSB3aGVuIHRoZSBjbHVzdGVyIG5hbWVzIGFyZSBzd2FwcGVkIG91dCBmb3Igc3R1ZmYgbGlrZSAicHJvZ2VuaXRvcnMiIGFuZCAiY29uZXMiCgpUaGlzIGFsc28gbWF5IGJlIHRvdGFsIHRyYXNoIC0gSSBjYW4ndCByZW1lbWJlciB3aGF0IGNsdXN0ZXIgaXMgZG9pbmcgd2hhdC4uLnNvIHBlcmhhcHMgdGhlc2UgcmVsYXRpb25zaGlwcyBtYWtlIG5vIHNlbnNlPwpgYGB7cn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImZpZ3VyZXMvc2N2ZWxvX1dULlBBR0EucG5nIikKa25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImZpZ3VyZXMvc2N2ZWxvX0cxNS5QQUdBLnBuZyIpCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJmaWd1cmVzL3NjdmVsb19HMTYuUEFHQS5wbmciKQpgYGAKCgo8IS0tICMgQ2VsbFJhbmsgKG5vdCB1c2VmdWw/KSAtLT4KCgo8IS0tIGBgYHtweXRob259IC0tPgo8IS0tIGFkYXRhID0gc2MucmVhZF9oNWFkKCcvVXNlcnMvbWNnYXVnaGV5ZC9kYXRhL2NoaWNrX21pcnVuYS9tMDA3X3NjVmVsby5oNWFkJykgLS0+CjwhLS0gY3IudGwudGVybWluYWxfc3RhdGVzKGFkYXRhLCBjbHVzdGVyX2tleT0ic2V1cmF0X2NsdXN0ZXJzIiwgd2VpZ2h0X2Nvbm5lY3Rpdml0aWVzPTAuNCwgbl9qb2JzID0gMSwgYmFja2VuZD0ndGhyZWFkaW5nJykgLS0+CjwhLS0gI2NyLnBsLnRlcm1pbmFsX3N0YXRlcyhhZGF0YSkgLS0+Cgo8IS0tIGNyLnRsLmluaXRpYWxfc3RhdGVzKGFkYXRhLCBjbHVzdGVyX2tleT0ic2V1cmF0X2NsdXN0ZXJzIiwgbl9qb2JzID0gMSwgYmFja2VuZD0ndGhyZWFkaW5nJykgLS0+CjwhLS0gI2NyLnBsLmluaXRpYWxfc3RhdGVzKGFkYXRhLCBkaXNjcmV0ZT1UcnVlKSAtLT4KCjwhLS0gY3IudGwubGluZWFnZXMoYWRhdGEpIC0tPgo8IS0tICNjci5wbC5saW5lYWdlcyhhZGF0YSwgc2FtZV9wbG90PUZhbHNlKSAtLT4KCgo8IS0tIHNjdi50bC5yZWNvdmVyX2xhdGVudF90aW1lKGFkYXRhLCByb290X2tleT0iaW5pdGlhbF9zdGF0ZXNfcHJvYnMiLCBlbmRfa2V5PSJ0ZXJtaW5hbF9zdGF0ZXNfcHJvYnMiKSAtLT4KPCEtLSBzY3YudGwucGFnYSggLS0+CjwhLS0gICAgIGFkYXRhLCAtLT4KPCEtLSAgICAgZ3JvdXBzPSJzZXVyYXRfY2x1c3RlcnMiLCAtLT4KPCEtLSAgICAgcm9vdF9rZXk9ImluaXRpYWxfc3RhdGVzX3Byb2JzIiwgLS0+CjwhLS0gICAgIGVuZF9rZXk9InRlcm1pbmFsX3N0YXRlc19wcm9icyIsIC0tPgo8IS0tICAgICB1c2VfdGltZV9wcmlvcj0idmVsb2NpdHlfcHNldWRvdGltZSIsIC0tPgo8IS0tICkgLS0+CgoKPCEtLSBjci5wbC5jbHVzdGVyX2ZhdGVzKCAtLT4KPCEtLSAgICAgYWRhdGEsIC0tPgo8IS0tICAgICBtb2RlPSJwYWdhX3BpZSIsIC0tPgo8IS0tICAgICBjbHVzdGVyX2tleT0ic2V1cmF0X2NsdXN0ZXJzIiwgLS0+CjwhLS0gICAgIGJhc2lzPSJ1bWFwIiwgLS0+CjwhLS0gICAgIG5vZGVfc2l6ZV9zY2FsZT01LCAtLT4KPCEtLSAgICAgZWRnZV93aWR0aF9zY2FsZT0xLCAtLT4KPCEtLSAgICAgbWF4X2VkZ2Vfd2lkdGg9NCwgcGFsZXR0ZT1jb2xvcl9wYWxldHRlLCBkcGkgPSAzMDAsIC0tPgo8IS0tICAgICB0aXRsZT0iZGlyZWN0ZWQgUEFHQSIsIHNob3cgPSBGYWxzZSwgc2F2ZSAgPSAncGllLnRlc3QucG5nJyAtLT4KPCEtLSApIC0tPgo8IS0tIGBgYCAtLT4K